package com.kmt.plaza.sevice.impl;

import com.kmt.plaza.dao.OrderItemMapper;
import com.kmt.plaza.dao.OrdersMapper;
import com.kmt.plaza.dao.ProductSkuMapper;
import com.kmt.plaza.dao.ShoppingCartMapper;
import com.kmt.plaza.entity.OrderItem;
import com.kmt.plaza.entity.Orders;
import com.kmt.plaza.entity.ProductSku;
import com.kmt.plaza.sevice.OrderService;
import com.kmt.plaza.utils.PageHelper;
import com.kmt.plaza.vo.*;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private ShoppingCartMapper shoppingCartMapper;
    @Autowired
    private OrdersMapper ordersMapper;
    @Autowired
    private OrderItemMapper orderItemMapper;
    @Autowired
    private ProductSkuMapper productSkuMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    private Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class);
    @Transactional
    //添加订单
    public HashMap<String,String> addOrder(String cids, Orders order) throws SQLException {
        logger.info("开始添加订单......");
        HashMap<String,String> map = new HashMap<>();
        //1、根据cids查询当前订单中关联的购物车记录详情
        String[] arr = cids.split(",");
        List<Integer> cidsList = new ArrayList<>();
        for (int i = 0; i <arr.length ; i++) {
            cidsList.add(Integer.parseInt(arr[i]));
        }
        //获取到所有购物车记录（业务部分），获取所有的SKUID
        //根据用户在购物车列表中选择的购物车记录的id ，查询对应的购物车记录
        List<ShoppingCartVO> list = shoppingCartMapper.selectShoppingCartBycids(cidsList);
        //从购车信息中获取到购买的的skuId(也就是商品Id)，以skuId为key写到redis中：
        boolean isLock = true;//开启锁
        String[] skuIds = new String[list.size()];
        HashMap<String, RLock> locksMap = new HashMap<>();//存放成功的锁
        for (int i = 0; i <list.size() ; i++) {
            //订单中可能包含多个商品，每个skuid表示一个商品
            String skuId = String.valueOf(list.get(i).getSkuId());
            boolean b = false;
            try {
                //构建当前商品的锁
                RLock lock = redissonClient.getLock(skuId);
                //尝试加锁，最多等待10s,上锁后三秒自动解锁
                b = lock.tryLock(10,3, TimeUnit.SECONDS);
                if(b){//如果加锁成功
                    skuIds[i] = skuId;
                    locksMap.put(skuId,lock);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            isLock = isLock && b;
        }
        //如果isLock为true,表示”加锁“成功
        if(isLock){
            logger.info("开始校验库存......");
            //2、校验库存
            //当第一次查询购物车记录之后，在加锁成功之前，可能被其他的并发线程修改库存，故要进行第二次查询
            list = shoppingCartMapper.selectShoppingCartBycids(cidsList);
            boolean f = true;
            String untitled = "";
            for(ShoppingCartVO sc : list){
                if(Integer.parseInt(sc.getCartNum())>sc.getStock()){
                    f = false;//库存不足将f设置为false
                }
                //拼接多个商品名称
                untitled = untitled+sc.getProductName()+",";
            }
            if(f){
                //表示库存充足
                order.setUntitled(untitled);//以untitled作为订单名称
                order.setCreateTime(new Date());//创建订单时间
                order.setStatus("1");//设置订单状态为待支付
                //生成订单的编号
                logger.info("开始生成订单编号......");
                String orderId = UUID.randomUUID().toString().replace("-", "");//唯一标识
                order.setOrderId(orderId);
                //保存订单
                logger.info("开始保存订单......");
                ordersMapper.insert(order);
                //生成商品快照
                logger.info("开始生成商品快照......");
                for(ShoppingCartVO sc : list){
                    int cnum = Integer.parseInt(sc.getCartNum());
                    String itemId = System.currentTimeMillis()+""+ (new Random().nextInt(89999)+10000);
                    OrderItem orderItem = new OrderItem(itemId,orderId,sc.getProductId(), sc.getProductName(),sc.getProductImg(),sc.getSkuId(),sc.getSkuName(),new BigDecimal(sc.getSellPrice()),cnum,new BigDecimal(sc.getSellPrice()*cnum),new Date(), new Date(),0);
                    //保存订单快照
                    orderItemMapper.insert(orderItem);
                }
                //5、扣减库存：根据套餐id修改套餐库存量
                logger.info("开始扣减库存......");
                for(ShoppingCartVO sc : list){
                    int skuId = sc.getSkuId();
                    int newStock = sc.getStock()-Integer.parseInt(sc.getCartNum());
                    ProductSku productSku = new ProductSku();
                    productSku.setSkuId(skuId);
                    productSku.setStock(newStock);
                    productSkuMapper.updateByPrimaryKeySelective(productSku);
                }
                //删除购物车，当购物车中的记录购买成功后，购物车中对应做删除操作
                logger.info("开始删除购物车记录......");
                for(int cid : cidsList){
                    shoppingCartMapper.deleteByPrimaryKey(cid);
                }
                //释放锁
                for (int j = 0; j< skuIds.length; j++) {
                    String skuId = skuIds[j];
                    //比较锁，是自己的锁才释放
                    if(skuId!=null && !"".equals(skuId)){
                        locksMap.get(skuId).unlock();
                    }
                }
                logger.info("订单添加成功......");
                map.put("orderId",orderId);
                map.put("productNames",untitled);
                return map;
            }else {
                //表示库存不足
                return null;
            }
        }else{
            //加锁失败。订单添加失败
            //当加锁失败时，有可能对部分商品已经锁定，要释放锁定的部分商品
            for (int i = 0; i < skuIds.length; i++) {
                String skuId = skuIds[i];
                if(skuId!=null && !"".equals(skuId)){
                    locksMap.get(skuId).unlock();
                }
            }
            return null;
        }
//问题1：如果订单中部分商品加锁成功，某一个加锁失败，导致最终加锁状态失败——>需要对已经锁定的部分商品释放锁
//问题2：在成功加锁之前，我们根据购物车的id查询了购物车记录（包含商品库存），能否直接使用这个库存进行库存校验？
        //---不能，因为在查询之后加锁之前，有可能被并发的线程修改了库存，因此在比较之前需要重新查询。
//问题3：在当前线程加锁成功之后，执行添加订单的过程中，如果当前线程出现异常，导致无法释放锁，这一问题该如何解决？
        //解决方案：在对商品加锁时，设置过期时间，这样一来即使线程出现故障无法释放锁，在过期时间结束时也会自动释放
//问题四：当给锁设置了过期时间之后，如果当前线程因为别的原因，在锁过期之前没有完成业务执行，将会释放锁，同时其他线程（t2）就会加锁成功了，
// 当t2加锁成功后，t1执行结束后释放锁，就会释放t2的锁，就会导致t2在无锁状态下执行业务
        //解决方案1：在加锁的时候同时为锁设置唯一的value,在释放锁的时候，先获取当前商品在redis中的value,如果获取的值和当前value相同，则释放锁
        //解决方案2：看门狗机制（watchdog）,为当前线程创建守护线程，如果在业务为执行完之前锁过期了，可以采用守护线程进行续命，从而保证在当前线程业务未执行完之前锁不会过期，只有业务完成释放锁之后才会停止。


    }
    @Override
    //更新订单状态
    public int updateOrderStatus(String orderId, String status) {
        logger.info("更新订单状态......");
        Orders orders = new Orders();
        orders.setOrderId(orderId);
        orders.setStatus(status);
        int i = ordersMapper.updateByPrimaryKeySelective(orders);
        return i;
    }

    /**
     * 更改订单状态为已评价
     * @param orderId
     * @return
     */
    @Override
    public int updateOrderStatu(String orderId) {
        logger.info("更改订单状态为已评价开始......");
        return 0;
    }

    @Override
    //根据订单id获取订单信息
    public ResultVO getOrderById(String orderId) {
        logger.info("根据订单id获取订单信息开始......");
        Orders order = ordersMapper.selectByPrimaryKey(orderId);
        return new ResultVO(ResStatus.OK,"success",order.getStatus());
    }
    @Override
    //查询订单列表
    public ResultVO OrderList(String userId, String status, int pageNum, int limit) {
        logger.info("查询订单列表开始......");
        //1、分页查询
        int start = (pageNum - 1)* limit;
        List<OrderVO> orderList = ordersMapper.selcetOrderList(userId, status, start, limit);
        //2、查询总记录数
        Example example = new Example(Orders.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andLike("userId",userId);
        if(status != null && !"".equals(status)){
            criteria.andLike("status",status);
        }
        int count = ordersMapper.selectCountByExample(example);
        //3、计算页数
        int pageCount = count % limit == 0 ? count / limit : count / limit + 1;
        //4、封装数据
        PageHelper<OrderVO> pageHelper = new PageHelper<>(count, pageCount, orderList);
        return new ResultVO(ResStatus.OK,"success",pageHelper);
    }
    @Override
    public ResultVO selectAllOrderList(int pageNum, int limit) {
        logger.info("查询所有订单列表开始......");
        Example example = new Example(Orders.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("");
        int count = ordersMapper.selectCountByExample(example);
        //2、计算总页数
        int pageCount = count%limit == 0?count/limit : (count/limit)+1;

        //3、查询当前页的数据
        int start = (pageNum-1)*limit;
        List<OrderVO> orderList = ordersMapper.selcetOrderLists(start, limit);
        ResultVO resultVO = new ResultVO(ResStatus.OK, "success", new PageHelper<OrderVO>(count, pageCount, orderList));
        return resultVO;
    }

    @Override
    public ResultVO selectByStatus(int pageNum, int limit) {
        logger.info("按订单状态查询......");
        //查询全部订单
        Example examples = new Example(Orders.class);
        Example.Criteria criterias = examples.createCriteria();
        criterias.andEqualTo("");
        int allOrder = ordersMapper.selectCountByExample(examples);
        //查询状态为1(待付款)的订单
        Example example = new Example(Orders.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("status",1);
          //查询状态为1的订单
        int obligation = ordersMapper.selectCountByExample(example);
        //查询状态为2(待发货)的订单
        Example example1 = new Example(Orders.class);
        Example.Criteria criteria1 = example1.createCriteria();
        criteria1.andEqualTo("status",2);
        int consignment = ordersMapper.selectCountByExample(example1);
        //查询状态为3(待收货)的订单
        Example example2 = new Example(Orders.class);
        Example.Criteria criteria2 = example2.createCriteria();
        criteria2.andEqualTo("status",3);
        int waitDelivery = ordersMapper.selectCountByExample(example2);
        //查询状态为4(待评价)的订单
        Example example3 = new Example(Orders.class);
        Example.Criteria criteria3 = example3.createCriteria();
        criteria3.andEqualTo("status",4);
        int evaluate = ordersMapper.selectCountByExample(example3);
        //查询状态为5(已完成)的订单
        Example example4 = new Example(Orders.class);
        Example.Criteria criteria4 = example4.createCriteria();
        criteria4.andEqualTo("status",5);
        int Completed = ordersMapper.selectCountByExample(example4);
        //查询状态为6(已关闭)的订单
        Example example5 = new Example(Orders.class);
        Example.Criteria criteria5 = example5.createCriteria();
        criteria5.andEqualTo("status",6);
        int closed = ordersMapper.selectCountByExample(example5);
        //查询状态为7(已评价)的订单
        Example example6 = new Example(Orders.class);
        Example.Criteria criteria6 = example6.createCriteria();
        criteria6.andEqualTo("status",7);
        int Evaluated = ordersMapper.selectCountByExample(example6);
        HashMap<String,Object> hashMap = new HashMap<>();
        hashMap.put("allOrder",allOrder);
        hashMap.put("obligation",obligation);
        hashMap.put("consignment",consignment);
        hashMap.put("waitDelivery",waitDelivery);
        hashMap.put("evaluate",evaluate);
        hashMap.put("Completed",Completed);
        hashMap.put("closed",closed);
        hashMap.put("Evaluated",Evaluated);
//        //计算页面
//        int pageCount = count % limit == 0 ? count / limit : count / limit + 1;
//        PageHelper<String,Orders> pageHelper = new PageHelper<String,Orders>(count, pageCount, hashMap);
        return new ResultVO(ResStatus.OK,"success",hashMap);
    }

    @Override
    public ResultVO deleteByOrderId(String orderId) {
        logger.info("根据orderId删除订单......");
        Example example = new Example(Orders.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("orderId",orderId);
        int i = ordersMapper.deleteByExample(example);
        if(i>0){
            return new ResultVO(ResStatus.OK,"success！",i);
        }else {
            return new ResultVO(ResStatus.NO,"fail！",null);
        }
    }

    @Override
    public ResultVO selectByOrderCondition(String keyWord, String status, Integer payType, String receiverName) {
        logger.info("管理员按条件开始......");
        List<Orders> orders = ordersMapper.queryOrderKey(keyWord, status, payType, receiverName);
        if(orders.size()>0){
            return new ResultVO(ResStatus.OK,"success",orders);
        }else{
            return new ResultVO(ResStatus.NO,"fail",null);
        }
    }

    @Override
    public int deleteUser(String orderId) {
        logger.info("订单删除成功......");
        Example example = new Example(Orders.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("orderId",orderId);
        int i = ordersMapper.deleteByExample(example);
        if(i>0){
            return i;
        }else {
            return 0;
        }

    }

    @Override
    public int TopayUpdateOrderStatus(String orderId) {
        logger.info("点击去支付改变订单状态......");
        int i = ordersMapper.ToPayOrderStatus(orderId);
        if(i>0){
            return 1;
        }else {
            return 0;
        }
    }

    @Override
    public int immediateDelivery(String orderId) {
        logger.info("管理员发货开始......");
        int i = ordersMapper.immediateDelivery(orderId);
        if(i>0){
            return 1;
        }else {
            return 0;
        }
    }

    @Override
    public int ConfirmReceipt(String orderId) {
        logger.info("管理员发货开始......");
        int i = ordersMapper.ConfirmReceipt(orderId);
        if(i>0){
            return 1;
        }else {
            return 0;
        }
    }


}
